package 剑指offer;

public class _33丑数 {
	
	/*
	 * 只包含因子2,3,5的正整数被称作丑数，比如4,10,12都是丑数，而7,23,111则不是丑数，另外1也是丑数。
	 * 把只包含质因子2、3和5的数称作丑数（Ugly Number）。
	 * 例如6(2*3)、8(2*2*2)都是丑数，但14不是，因为它包含质因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
	 */
		//思路:乘2或3或5，之后比较取最小值,丑数的定义是1或者因子只有2 3 5，可推出丑数=丑数*丑数
	/*
	 * 判断方法:
		首先除2，直到不能整除为止，然后除5到不能整除为止，然后除3直到不能整除为止。
		最终判断剩余的数字是否为1，如果是1则为丑数，否则不是丑数

		首先从丑数的定义我们知道，一个丑数的因子只有2,3,5，那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z，
		换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到，那么我们从1开始乘以2,3,5，就得到2,3,5三个丑数，
		在从这三个丑数出发乘以2,3,5就得到4，6,10,6，9,15,10,15,25九个丑数，我们发现这种方法会得到重复的丑数，而且我们题目要求第N个丑数，
		这样的方法得到的丑数也是无序的。那么我们可以维护三个队列：
		（1）丑数数组： 1
		乘以2的队列：2
		乘以3的队列：3
		乘以5的队列：5
		选择三个队列头最小的数2加入丑数数组，同时将该最小的数乘以2,3,5放入三个队列；
		（2）丑数数组：1,2
		乘以2的队列：4
		乘以3的队列：3，6
		乘以5的队列：5，10
		选择三个队列头最小的数3加入丑数数组，同时将该最小的数乘以2,3,5放入三个队列；
		（3）丑数数组：1,2,3
		乘以2的队列：4,6
		乘以3的队列：6,9
		乘以5的队列：5,10,15
		选择三个队列头里最小的数4加入丑数数组，同时将该最小的数乘以2,3,5放入三个队列；
		（4）丑数数组：1,2,3,4
		乘以2的队列：6，8
		乘以3的队列：6,9,12
		乘以5的队列：5,10,15,20
		选择三个队列头里最小的数5加入丑数数组，同时将该最小的数乘以2,3,5放入三个队列；
		（5）丑数数组：1,2,3,4,5
		乘以2的队列：6,8,10，
		乘以3的队列：6,9,12,15
		乘以5的队列：10,15,20,25
		选择三个队列头里最小的数6加入丑数数组，但我们发现，有两个队列头都为6，所以我们弹出两个队列头，同时将12,18,30放入三个队列；
		……………………
		疑问：
		1.为什么分三个队列？
		丑数数组里的数一定是有序的
		因为我们是从丑数数组里的数乘以2,3,5选出的最小数，一定比以前未乘以2,3,5大，同时对于三个队列内部，按先后顺序乘以2,3,5分别放入，所以同一个队列内部也是有序的；
		2.为什么比较三个队列头部最小的数放入丑数数组？
		(核心)
		因为三个队列是有序的，所以取出三个头中最小的，等同于找到了三个队列所有数中最小的。
		实现思路：
		我们没有必要维护三个队列，只需要记录三个指针显示到达哪一步；“|”表示指针,arr表示丑数数组；
		（1）1
		|2
		|3
		|5
		目前指针指向0,0,0，队列头arr[0] * 2 = 2,  arr[0] * 3 = 3,  arr[0] * 5 = 5
		（2）1 2
		2 |4
		|3 6
		|5 10
		目前指针指向1,0,0，队列头arr[1] * 2 = 4,  arr[0] * 3 = 3, arr[0] * 5 = 5
		（3）1 2 3
		2| 4 6
		3 |6 9
		|5 10 15
		目前指针指向1,1,0，队列头arr[1] * 2 = 4,  arr[1] * 3 = 6, arr[0] * 5 = 5
	 */
	//求按从小到大的顺序的第N个丑数。
    //思路:丑树都是由2,3,5互相乘于得来的,所有使用2,3,5三个数组来维护(我们没有必要维护三个队列，只需要记录三个指针显示到达哪一步,因为每次找到的都是最小值),先定义一个存放丑数顺序的数组{1,.....},从1开始开始存放,
    //每次都三个数组中找最小的值,在乘于2|3|5,判断哪个队列中为最小值,放入数组中,存放后,该数组的索引需要++,一直递归,返回arr[n-1]的值
	//多重if和 if else if 是有区别的, 多重if每个if都会判断 ,if else if如果满足不会进行后续判断
	 public int GetUglyNumber_Solution(int index) {
		 	if (index <= 0)
			 return 0;
		 	if(index<7)
			 return index;
			 int[] arr = new int[index];
			 arr[0] = 1;
			 int multiply2 = 0; //乘于2的队列
			 int multiply3 = 0;	//乘于3的队列
			 int multiply5 = 0;	//乘于5的队列
			//从1开始
			 for (int i = 1; i < index; i++) {
			// 选择三个队列头最小的数 
			 int min = Math.min(arr[multiply2] * 2,Math.min(arr[multiply3] * 3,arr[multiply5] * 5));
			 //将arr进行排好序
			 arr[i] = min;
			 if (arr[multiply2] * 2 == min) //不能用 else if 代替 多重if
			 multiply2++;
			 if (arr[multiply3] * 3 == min)
			 multiply3++;
			 if (arr[multiply5] * 5 == min)
			 multiply5++;
			 }
			 //索引值需要减一
			 return arr[index - 1];
	    }
	 public static int GetUglyNumber_Solution2(int index) {
		if(index <= 0) return 0;
		if( index<7) return index;
		int[] arr = new int[index];
		arr[0]=1;
		int multiply2 =0;
		int multiply3 =0;
		int multiply5 =0;
		 for (int i = 1; i <index ; i++) {
				int min = Math.min(arr[multiply2]*2,Math.min(arr[multiply3]*3,arr[multiply5]*5));
		 		arr[i] =min;
		 		//判断是哪个相等
			 //多重if和 if else if 是有区别的, 多重if每个if都会判断 ,if else if如果满足不会进行后续判断
		 		if(arr[multiply2]*2 ==min){
					multiply2++;
				}
		 		if(arr[multiply3]*3 ==min) {
					multiply3++;
				}
		 		if(arr[multiply5]*5 ==min) {
					multiply5++;
				}
		 }
		 //从 0开始存值的,所以需要-1
		return arr[index-1];
	 }

	public static void main(String[] args) {

		GetUglyNumber_Solution2(10);
	}
}
